package com.youlu.campus.service.wechat.impl;

import com.alibaba.fastjson.JSON;
import com.google.gson.JsonObject;
import com.youlu.campus.base.exception.BusinessException;
import com.youlu.campus.entity.WechatAccessTokenPO;
import com.youlu.campus.entity.WechatConfigInfo;
import com.youlu.campus.entity.WechatH5UserPO;
import com.youlu.campus.entity.WechatMiniUserPO;
import com.youlu.campus.entity.domain.WechatAccessToken;
import com.youlu.campus.entity.domain.WechatJsApiTicketDomain;
import com.youlu.campus.entity.domain.WechatQRCode;
import com.youlu.campus.service.base.BaseService;
import com.youlu.campus.service.cache.CacheUserService;
import com.youlu.campus.service.wechat.WechatConfigInfoService;
import com.youlu.campus.service.wechat.WechatH5Service;
import lombok.extern.slf4j.Slf4j;
import net.polyv.live.util.HttpClientUtil;
import net.polyv.live.util.JsonUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class WechatH5ServiceImpl extends BaseService implements WechatH5Service {

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private WechatConfigInfoService wechatConfigInfoService;
    private String wechatAccessTokenUlr = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
    private String jsTicketPrefixKey = "com.luyou.wechat-h5.jsTicket.%s";
    private String jsTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi";

    private static final String ACCESS_TOKEN = "campus:access:token:appId:%s";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Lazy
    @Autowired
    private CacheUserService cacheUserService;


    @Override
    public boolean create(WechatAccessTokenPO req) {
        if (Objects.isNull(req)) {
            log.error(":>>> 参数错误");
            throw new BusinessException("参数错误");
        }
        req.setCreatedTime(new Date());
        mongoTemplate.insert(req);
        return true;
    }

    @Override
    public WechatAccessTokenPO getByAppId(String appId) {
        Query query = new Query().addCriteria(Criteria.where("appId").is(appId));
        return mongoTemplate.findOne(query, WechatAccessTokenPO.class);
    }

    @Override
    public boolean refresh(String appId) {
        // 加锁
        return false;
    }

    @Override
    public WechatAccessToken getAccessToken(String appId, String appSecret) {
        try {
            WechatAccessToken wechatAccessToken = this.getAccessTokenFromWechat(appId, appSecret);
            return wechatAccessToken;
//            Query query = new Query().addCriteria(Criteria.where("appId").is(appId));
//            WechatAccessTokenPO accessTokenPO = mongoTemplate.findOne(query, WechatAccessTokenPO.class);
//            if (Objects.isNull(accessTokenPO)) {
//                log.error(":>>> appId:{} accessToken 为空", appId);
//                WechatAccessToken wechatAccessToken = this.getAccessTokenFromWechat(appId, appSecret);
//                log.info(":>>> 获取WechatAccessToken:{}", JSON.toJSONString(wechatAccessToken));
//                if (Objects.isNull(wechatAccessToken.getExpireIn())) {
//                    wechatAccessToken.setExpireIn(7000);
//                }
//                WechatAccessTokenPO wechatAccessTokenPO = new WechatAccessTokenPO();
//                wechatAccessTokenPO.setAppId(appId);
//                wechatAccessTokenPO.setAccessToken(wechatAccessToken.getAccessToken());
//                wechatAccessTokenPO.setCreatedTime(new Date());
//                wechatAccessTokenPO.setExpiredTime(new Date(new Date().getTime() - wechatAccessToken.getExpireIn() * 1000 - 5 * 60 * 1000));
//                mongoTemplate.insert(wechatAccessTokenPO);
//                return wechatAccessToken;
//            }
//            if ((date.getTime() - accessTokenPO.getExpiredTime().getTime()) < 5 * 60 * 1000L) {
//                // 刷新Token
//                log.info(":>>> accessToken 快过期,开始刷新Token");
//                WechatAccessToken wechatAccessToken = this.getAccessTokenFromWechat(appId, appSecret);
//                Update update = new Update();
//                update.set("accessToken", wechatAccessToken.getAccessToken());
//                update.set("expiredTime", new Date(date.getTime() - wechatAccessToken.getExpireIn() * 1000 - 5 * 60 * 1000));
//                update.set("updatedTime", date);
//                mongoTemplate.updateFirst(query, update, WechatAccessTokenPO.class);
//                return wechatAccessToken;
//            }
//            WechatAccessToken wechatAccessToken = new WechatAccessToken();
//            wechatAccessToken.setAccessToken(accessTokenPO.getAccessToken());
//            return wechatAccessToken;
        } catch (Exception e) {
            log.error(":>>> 获取AccessToken失败:{}", e);
            throw new BusinessException("获取AccessToken失败");
        }
    }

    @Override
    public String getJsapiTicket(String appId) {
        log.info(":>>> 开始获取JsapiTicket");
        WechatConfigInfo configInfo = wechatConfigInfoService.find(appId);
        if (Objects.isNull(configInfo)) {
            log.error(":>>> 系统配置错误:appId无配置", appId);
        }
        WechatAccessToken token = this.getAccessToken(appId, configInfo.getAppSecret());
        log.info(":>>> 获取的AccessToken :{}", JSON.toJSONString(token));
        if (Objects.isNull(token)) {
            log.error(":>>> 获取的Token 为空:{}");
            throw new BusinessException("获取的AccessToken");
        }
        Object jsApiTicket = redisTemplate.opsForValue().get(String.format(jsTicketPrefixKey, appId));
        if (Objects.isNull(jsApiTicket)) {
            WechatJsApiTicketDomain ticket = this.getJsapiTicketByAccessToken(token.getAccessToken());
            if (Objects.isNull(ticket)) {
                log.error(":>>> WechatJsApiTicket 为空:{}");
                throw new BusinessException("获取的WechatJsApiTicket失败");
            }
            if (ticket.getErrcode().intValue() == 42001) {
                this.deleteAccessToken(appId);
                ticket = this.getJsapiTicketByAccessToken(token.getAccessToken());

            }
            if (Objects.isNull(ticket.getExpires_in())) {
                ticket.setExpires_in(7000);
            }
            log.info(":>>>ticket:{}", JSON.toJSONString(ticket));
            redisTemplate.opsForValue().set(String.format(jsTicketPrefixKey, appId), ticket.getTicket(), ticket.getExpires_in() - 200, TimeUnit.SECONDS);
            return ticket.getTicket();

        }
        return (String) jsApiTicket;
    }

    @Override
    public String createForeverTicket(String appId, String sceneStr) {
        Map<String, String> params = new HashMap<>();
        WechatConfigInfo configInfo = wechatConfigInfoService.find(appId);
        WechatAccessToken accessToken = this.getAccessToken(appId, configInfo.getAppSecret());
        params.put("access_token", accessToken.getAccessToken());
        // output data
        JsonObject data = new JsonObject();
        data.addProperty("action_name", "QR_LIMIT_STR_SCENE");
        JsonObject actionInfo = new JsonObject();
        JsonObject scene = new JsonObject();
        scene.addProperty("scene_str", sceneStr);
        actionInfo.add("scene", scene);
        data.add("action_info", actionInfo);
        log.info(":>>> 创建永久二维码参数:{},{},{}", JSON.toJSONString(params), data);
        String result = HttpClientUtil.getInstance().sendHttpPost("https://api.weixin.qq.com/cgi-bin/qrcode/create", params, data.toString());
        log.info(":>>> 获取二维码信息:{}", result);
        WechatQRCode qrcode = JSON.parseObject(result, WechatQRCode.class);
        if (Objects.nonNull(qrcode.getErrcode()) && qrcode.getErrcode().intValue() == 42001) {
            deleteAccessToken(appId);
            log.error(":>>> token 过期:{}", appId);
        }
        return qrcode == null ? null : qrcode.getUrl();
    }

    @Override
    public WechatH5UserPO findByUnionId(String unionId) {
        log.info(":>>> 开始通过UnionId查询H5用户:{}", unionId);
        Query query = new Query().addCriteria(Criteria.where("unionId").is(unionId));
        WechatH5UserPO wechatH5UserPO = mongoTemplate.findOne(query, WechatH5UserPO.class);
        return wechatH5UserPO;
    }

    @Override
    public List<WechatH5UserPO> selectByUnionId(String unionId) {
        Query query = new Query().addCriteria(Criteria.where("unionId").is(unionId));
        query.with(Sort.by(Sort.Order.desc("id")));
        return mongoTemplate.find(query, WechatH5UserPO.class);
    }

    @Override
    public WechatH5UserPO findByOpenId(String openId) {
        Query query = new Query().addCriteria(Criteria.where("openId").is(openId));
        return mongoTemplate.findOne(query, WechatH5UserPO.class);
    }

    @Override
    public List<WechatH5UserPO> findByUserId(String userId) {
        Query query = new Query().addCriteria(Criteria.where("accountInfoId").is(userId));
        return mongoTemplate.find(query, WechatH5UserPO.class);
    }

    @Override
    public boolean deleteByUserId(String userId) {
        Query query = new Query().addCriteria(Criteria.where("accountInfoId").is(userId));
        List<WechatH5UserPO> wechatH5UserPOS = mongoTemplate.find(query, WechatH5UserPO.class);
        for (WechatH5UserPO wechatH5UserPO : wechatH5UserPOS) {
            if(null!=wechatH5UserPO.getOpenId()){
                cacheUserService.removeWechatH5UserOpenId(wechatH5UserPO.getOpenId());
            }
        }
        return mongoTemplate.remove(query, WechatH5UserPO.class).getDeletedCount() > 0 ? true : false;
    }

    @Override
    public boolean deleteMiniUser(String userId) {
        log.info(":>>> 删除微信小程序:{}",userId);
        Query query = new Query().addCriteria(Criteria.where("accountInfoId").is(userId));
        return mongoTemplate.remove(query, WechatMiniUserPO.class).getDeletedCount() > 0 ? true : false;
    }

    private WechatAccessToken getAccessTokenFromWechat(String appId, String appSecret) {
        String accessTokenKey = String.format(ACCESS_TOKEN, appId);
        String accessToken = stringRedisTemplate.opsForValue().get(accessTokenKey);
        if (StringUtils.isNotBlank(accessToken)) {
            WechatAccessToken wechatAccessToken = new WechatAccessToken();
            wechatAccessToken.setAccessToken(accessToken);
            return wechatAccessToken;
        }
        String accessTokenStr = HttpClientUtil.getInstance().sendHttpGet(String.format(wechatAccessTokenUlr, appId, appSecret));
        log.info(":>>> 获取AccessToken:{}", accessTokenStr);
        if (StringUtils.isBlank(accessTokenStr)) {
            log.error(":>>> 获取AccessToken失败");
            return null;
        }
        WechatAccessToken wechatAccessToken = JSON.parseObject(accessTokenStr, WechatAccessToken.class);
        if (Objects.isNull(wechatAccessToken)) {
            throw new BusinessException("获取AccessToken失败");
        }
        redisTemplate.opsForValue().set(accessTokenKey, wechatAccessToken.getAccessToken(), 7200 - 5, TimeUnit.SECONDS);
        return wechatAccessToken;
    }

    private WechatJsApiTicketDomain getJsapiTicketByAccessToken(String accessToken) {
        String wechatJsApiTicket = HttpClientUtil.getInstance().sendHttpGet(String.format(jsTicketUrl, accessToken));
        log.info(":>>> 获取wechatJsApiTicket:{}", wechatJsApiTicket);
        if (StringUtils.isBlank(wechatJsApiTicket)) {
            log.error(":>>> 获取wechatJsApiTicket失败");
            return null;
        }
        WechatJsApiTicketDomain wechatJsApiTicketDomain = JSON.parseObject(wechatJsApiTicket, WechatJsApiTicketDomain.class);
        if (Objects.isNull(wechatJsApiTicketDomain)) {
            throw new BusinessException("获取WechatJsApiTicket失败");
        }
        return wechatJsApiTicketDomain;
    }

    private void deleteAccessToken(String appId) {
        Query query = new Query().addCriteria(Criteria.where("appId").is(appId));
        mongoTemplate.remove(query, WechatAccessTokenPO.class);
        log.info(":>>> 删除:accessToken");
    }
}
